#include <dspSimulator/instruction/mvk.h>

bool MVK::checkCondition(std::shared_ptr<DspMachine> dspMachine) {
    if (isCompactInstruction) {
        if (compactInstKind == "LSDx1c") {
            return checkLSDx1cCondition(dspMachine);
        }
        return true;
    } else {
        return checkStandardCondition(dspMachine);
    }
}

void MVK::loadInstIRIntoJit(llvm::LLVMContext *llvmContext, llvm::Module *module,
                            std::shared_ptr<llvm::IRBuilder<>> irBuilder) {
    llvm::FunctionType *funcType = llvm::FunctionType::get(irBuilder->getVoidTy(), false);

    // --------------------------------- MVK (.unit) scst16,sint ---------------------------------------------
    {
        llvm::Function *mvkS = llvm::Function::Create(funcType, llvm::GlobalValue::ExternalLinkage, "MVKKind1",
                                                      module);
        llvm::BasicBlock *entryBB = llvm::BasicBlock::Create(*llvmContext, "entry", mvkS);
        irBuilder->SetInsertPoint(entryBB);

        auto dstRegPtr = getDstLoRegPtr(module, irBuilder);
        auto cst16 = getCstN(module, irBuilder, 16);
        auto cst16SExt = irBuilder->CreateSExt(cst16, irBuilder->getInt32Ty());

        irBuilder->CreateStore(cst16SExt, dstRegPtr);
        irBuilder->CreateRetVoid();
    }

    // --------------------------------- MVK (.unit) scst5,sint ---------------------------------------------
    {
        llvm::Function *mvkL = llvm::Function::Create(funcType, llvm::GlobalValue::ExternalLinkage, "MVKKind2",
                                                      module);
        llvm::BasicBlock *entryBB = llvm::BasicBlock::Create(*llvmContext, "entry", mvkL);
        irBuilder->SetInsertPoint(entryBB);

        auto dstRegPtr = getDstLoRegPtr(module, irBuilder);
        auto cst5 = getCstN(module, irBuilder, 5);
        auto cst5SExt = irBuilder->CreateSExt(cst5, irBuilder->getInt32Ty());

        irBuilder->CreateStore(cst5SExt, dstRegPtr);
        irBuilder->CreateRetVoid();
    }
}

std::shared_ptr<Instruction> MVK::decode(std::shared_ptr<InstInfo> instInfo, u64 baseCycle) {
    std::shared_ptr<MVK> mvk;
    auto dspInstInfo = std::dynamic_pointer_cast<DspInstInfo>(instInfo);
    bool isStandardInst = dspInstInfo->isStandardInstruction();
    if (isStandardInst) {
        // Standard指令
        auto data = vectorToBitSet<32>(dspInstInfo->getBits());
        if (checkBits<5>(data, 2, 0b01010) && existStandardCondition(data)) {
            // MVK (.unit) scst16, sint
            mvk = std::make_shared<MVK>(dspInstInfo->getAddress(), baseCycle);
            mvk->creg_z = extractBits<4>(data, 28);
            mvk->dst = extractBits<5>(data, 23).to_ulong();
            mvk->cst = extractBits<16>(data, 7).to_ulong();
            mvk->s = data[1];
            mvk->p = data[0];
            if (mvk->s == 0) {
                mvk->funcUnit = FuncUnit::S1;
            } else {
                mvk->funcUnit = FuncUnit::S2;
            }
            mvk->instFormat = InstFormat::Kind1;
        } else if (checkBits<10>(data, 2, 0b0011010110) && checkBits<5>(data, 13, 0b00101) &&
                existStandardCondition(data)) {
            // MVK (.unit) scst5, sint
            mvk = std::make_shared<MVK>(dspInstInfo->getAddress(), baseCycle);
            mvk->creg_z = extractBits<4>(data, 28);
            mvk->dst = extractBits<5>(data, 23).to_ulong();
            mvk->cst = extractBits<5>(data, 18).to_ulong();
            mvk->x = data[12];
            mvk->s = data[1];
            mvk->p = data[0];
            if (mvk->s == 0) {
                mvk->funcUnit = FuncUnit::L1;
            } else {
                mvk->funcUnit = FuncUnit::L2;
            }
            mvk->instFormat = InstFormat::Kind2;
        } else if (checkBits<11>(data, 2, 0b00000010000) && existStandardCondition(data)) {
            // MVK (.unit) scst5, sint
            mvk = std::make_shared<MVK>(dspInstInfo->getAddress(), baseCycle);
            mvk->creg_z = extractBits<4>(data, 28);
            mvk->dst = extractBits<5>(data, 23).to_ulong();
            mvk->src2 = extractBits<5>(data, 18).to_ulong();
            mvk->cst = extractBits<5>(data, 13).to_ulong();
            mvk->s = data[1];
            mvk->p = data[0];
            if (mvk->s == 0) {
                mvk->funcUnit = FuncUnit::L1;
            } else {
                mvk->funcUnit = FuncUnit::L2;
            }
            mvk->instFormat = InstFormat::Kind2;
        }
    } else {
        auto data = vectorToBitSet<16>(dspInstInfo->getBits());
        if (checkBits<6>(data, 1, 0b010011) && data[10] == 1) {
            mvk = std::make_shared<MVK>(dspInstInfo->getAddress(), baseCycle);
            mvk->isCompactInstruction = true;
            mvk->compactInstKind = "Lx5";
            mvk->cst = extractBits<3>(data, 13).to_ulong();
            mvk->cst += extractBits<2>(data, 11).to_ulong() << 3;
            mvk->s = data[0];
            mvk->dst = extractBits<3>(data, 7).to_ulong();
            mvk->PROT = dspInstInfo->getPROT();
            mvk->BR = dspInstInfo->getBR();
            mvk->DSZ = dspInstInfo->getDSZ();
            mvk->SAT = dspInstInfo->getSAT();
            mvk->RS = dspInstInfo->getRS();
            mvk->p = dspInstInfo->getP();
            fixUpRegOnCompactInstruction();
            if (mvk->s == 0) {
                mvk->funcUnit = FuncUnit::L1;
            } else {
                mvk->funcUnit = FuncUnit::L2;
            }
            mvk->instFormat = InstFormat::Kind2;
        } else if (checkBits<4>(data, 1, 0b1001)) {
            mvk = std::make_shared<MVK>(dspInstInfo->getAddress(), baseCycle);
            mvk->isCompactInstruction = true;
            mvk->compactInstKind = "Smvk8";
            mvk->cst = extractBits<3>(data, 13).to_ulong();
            mvk->cst += extractBits<2>(data, 11).to_ulong() << 3;
            mvk->cst += extractBits<2>(data, 5).to_ulong() << 5;
            mvk->cst += extractBits<1>(data, 10).to_ulong() << 7;
            mvk->dst = extractBits<3>(data, 7).to_ulong();
            mvk->s = data[0];
            if (mvk->s == 0) {
                mvk->funcUnit = FuncUnit::S1;
            } else {
                mvk->funcUnit = FuncUnit::S2;
            }
            mvk->PROT = dspInstInfo->getPROT();
            mvk->BR = dspInstInfo->getBR();
            mvk->DSZ = dspInstInfo->getDSZ();
            mvk->RS = dspInstInfo->getRS();
            mvk->SAT = dspInstInfo->getSAT();
            mvk->p = dspInstInfo->getP();
            mvk->fixUpRegOnCompactInstruction();
            mvk->instFormat = InstFormat::Kind1;
        } else if (checkBits<3>(data, 10, 0b010) && checkBits<2>(data, 5, 0b11) && checkBits<2>(data, 1, 0b11)) {
            mvk = std::make_shared<MVK>(dspInstInfo->getAddress(), baseCycle);
            mvk->isCompactInstruction = true;
            mvk->compactInstKind = "LSDx1c";
            mvk->CC = extractBits<2>(data, 14);
            mvk->cst = data[13];
            mvk->dst = extractBits<3>(data, 7).to_ulong();
            mvk->s = data[0];
            auto unit = extractBits<2>(data, 3);
            if (unit == 0b00) {
                mvk->funcUnit = FuncUnit::L1;
                if (mvk->s == 1)
                    mvk->funcUnit = FuncUnit::L2;
            } else if (unit == 0b01) {
                mvk->funcUnit = FuncUnit::S1;
                if (mvk->s == 1)
                    mvk->funcUnit = FuncUnit::S2;
            } else if (unit == 0b10) {
                mvk->funcUnit = FuncUnit::D1;
                if (mvk->s == 1)
                    mvk->funcUnit = FuncUnit::D2;
            }
            mvk->PROT = dspInstInfo->getPROT();
            mvk->BR = dspInstInfo->getBR();
            mvk->DSZ = dspInstInfo->getDSZ();
            mvk->RS = dspInstInfo->getRS();
            mvk->SAT = dspInstInfo->getSAT();
            mvk->p = dspInstInfo->getP();
            mvk->fixUpRegOnCompactInstruction();
            mvk->instFormat = InstFormat::Kind2;
        } else if (checkBits<6>(data, 10, 0b000110) && checkBits<2>(data, 5, 0b11) && checkBits<2>(data, 1, 0b11)) {
            mvk = std::make_shared<MVK>(dspInstInfo->getAddress(), baseCycle);
            mvk->isCompactInstruction = true;
            mvk->compactInstKind = "LSDx1";
            mvk->src2 = mvk->dst = extractBits<3>(data, 7).to_ulong();
            mvk->cst = 0;
            auto unit = extractBits<2>(data, 3).to_ulong();
            mvk->s = data[0];
            if (unit == 0b00) {
                mvk->funcUnit = FuncUnit::L1;
                if (mvk->s == 1)
                    mvk->funcUnit = FuncUnit::L2;
            } else if (unit == 0b01) {
                mvk->funcUnit = FuncUnit::S1;
                if (mvk->s == 1)
                    mvk->funcUnit = FuncUnit::S2;
            } else if (unit == 0b10) {
                mvk->funcUnit = FuncUnit::D1;
                if (mvk->s == 1)
                    mvk->funcUnit = FuncUnit::D2;
            }
            mvk->PROT = dspInstInfo->getPROT();
            mvk->BR = dspInstInfo->getBR();
            mvk->DSZ = dspInstInfo->getDSZ();
            mvk->RS = dspInstInfo->getRS();
            mvk->SAT = dspInstInfo->getSAT();
            mvk->p = dspInstInfo->getP();
            mvk->fixUpRegOnCompactInstruction();
            mvk->instFormat = InstFormat::Kind2;
        } else if (checkBits<6>(data, 10, 0b001110) && checkBits<2>(data, 5, 0b11) && checkBits<2>(data, 1, 0b11)) {
            mvk = std::make_shared<MVK>(dspInstInfo->getAddress(), baseCycle);
            mvk->isCompactInstruction = true;
            mvk->compactInstKind = "LSDx1";
            mvk->src2 = mvk->dst = extractBits<3>(data, 7).to_ulong();
            mvk->cst = 1;
            auto unit = extractBits<2>(data, 3).to_ulong();
            mvk->s = data[0];
            if (unit == 0b00) {
                mvk->funcUnit = FuncUnit::L1;
                if (mvk->s == 1)
                    mvk->funcUnit = FuncUnit::L2;
            } else if (unit == 0b01) {
                mvk->funcUnit = FuncUnit::S1;
                if (mvk->s == 1)
                    mvk->funcUnit = FuncUnit::S2;
            } else if (unit == 0b10) {
                mvk->funcUnit = FuncUnit::D1;
                if (mvk->s == 1)
                    mvk->funcUnit = FuncUnit::D2;
            }
            mvk->PROT = dspInstInfo->getPROT();
            mvk->BR = dspInstInfo->getBR();
            mvk->DSZ = dspInstInfo->getDSZ();
            mvk->RS = dspInstInfo->getRS();
            mvk->SAT = dspInstInfo->getSAT();
            mvk->p = dspInstInfo->getP();
            mvk->fixUpRegOnCompactInstruction();
            mvk->instFormat = InstFormat::Kind2;
        }
    }
    return mvk;
}

void MVK::executeCustom(std::shared_ptr<TargetMachine> targetMachine, ExecutePhase executePhase) {
    // 不需要自定义的执行方式
}

std::string MVK::toString() const {
    std::string res;
    if (isCompactInstruction) {
        if (compactInstKind == "LSDx1c") {
            res += getLSDx1cConditionString() + " ";
        }
    } else {
        res += getStandardConditionString() + " ";
    }
    res += m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)] + " ";
    if (instFormat == InstFormat::Kind1)
        res += dec2hex(extractBitsAndToInt<16>(cst)) + "," + getDstReg32Name();
    else if (instFormat == InstFormat::Kind2) {
        res += dec2hex(extractBitsAndToInt<5>(cst)) + "," + getDstReg32Name();
    }
    return res;
}

bool MVK::checkLSDx1cCondition(std::shared_ptr<DspMachine> dspMachine) {
    if (CC == 0b00) {
        return *dspMachine->getSaveRegs()->getReg32ByName("A0")->getDataPtr() != 0;
    } else if (CC == 0b01) {
        return *dspMachine->getSaveRegs()->getReg32ByName("A0")->getDataPtr() == 0;
    } else if (CC == 0b10) {
        return *dspMachine->getSaveRegs()->getReg32ByName("B0")->getDataPtr() != 0;
    } else {
        return *dspMachine->getSaveRegs()->getReg32ByName("B0")->getDataPtr() == 0;
    }
}

std::string MVK::getLSDx1cConditionString() const {
    if (CC == 0b00) {
        return {"[A0]"};
    } else if (CC == 0b01) {
        return {"[!A0]"};
    } else if (CC == 0b10) {
        return {"[B0]"};
    } else {
        return {"[!B0]"};
    }
}

std::shared_ptr<DspInstruction> MVK::clone() {
    return std::make_shared<MVK>(*this);
}
